#ifndef Texture3D_h
#define Texture3D_h

#include <Defines.h>
#include <CSharpPropertyMethods.h>

#include <System/RObject.h>
#include <System/RNotImplementedException.h>
#include <System/RArgumentNullException.h>

#include <Riccsson/Xna/Framework/Graphics/GraphicsDevice.h>
#include <Riccsson/Xna/Framework/Graphics/SurfaceFormat.h>

// OpenGL.
//#include "TextureTarget.h"
//#include "TextureUnit.h"

namespace Riccsson
{
	namespace Xna
	{
		namespace Framework
		{
			namespace Graphics
			{
				class Texture3D : public Texture
				{
					PROP3GET(Texture3D, int, Width)
					PROP3GET(Texture3D, int, Height)
					PROP3GET(Texture3D, int, Depth)

					private: int width;
					private: int height;
					private: int depth;
		
			//#if OPENGL
			//		PixelInternalFormat glInternalFormat;
			//		PixelFormat glFormat;
			//		PixelType glType;
			//#endif

					public: PROP3_GET(int, Width)
					{
						return width;
					}

					public: PROP3_GET(int, Height)
					{
						return height;
					}

					public: PROP3_GET(int, Depth)
					{
						return depth;
					}

					public: Texture3D (GraphicsDevice* graphicsDevice, int width, int height, int depth, bool mipMap, SurfaceFormat format)		
						: PROP3GET_INIT(Texture3D, Width)
						, PROP3GET_INIT(Texture3D, Height)
						, PROP3GET_INIT(Texture3D, Depth)
					{
						if (graphicsDevice == null)
							throw Riccsson::System::ArgumentNullException("graphicsDevice");

						this->graphicsDevice = graphicsDevice;
						this->width = width;
						this->height = height;
						this->depth = depth;
						this->levelCount = 1;

						/*
			#if OPENGL
						this->glTarget = TextureTarget.Texture3D;
            
						GL.GenTextures(1, out this->glTexture);
						GraphicsExtensions.CheckGLError();

						GL.BindTexture (glTarget, glTexture);
						GraphicsExtensions.CheckGLError();

						format.GetGLFormat (out glInternalFormat, out glFormat, out glType);

						GL.TexImage3D (glTarget, 0, glInternalFormat, width, height, depth, 0, glFormat, glType, IntPtr.Zero);
						GraphicsExtensions.CheckGLError();

						if (mipMap) 
							throw Riccsson::System::NotImplementedException("Texture3D does not yet support mipmaps.");
			#elif DIRECTX
						if (mipMap)
							this->levelCount = CalculateMipLevels(width, height, depth);

						var description = new Texture3DDescription
						{
							Width = width,
							Height = height,
							Depth = depth,
							MipLevels = levelCount,
							Format = SharpDXHelper.ToFormat(format),
							BindFlags = BindFlags.ShaderResource,
							CpuAccessFlags = CpuAccessFlags.None,
							Usage = ResourceUsage.Default,
							OptionFlags = ResourceOptionFlags.None,
						};

						_texture = new SharpDX.Direct3D11.Texture3D(graphicsDevice._d3dDevice, description);
			#endif
					*/
					}
		
					public: template<struct T> void SetData(Riccsson::System::Array<T>* data)
					{
						SetData<T>(data, 0, data.Length);
					}
		
					public: template<struct T> void SetData (Riccsson::System::Array<T>* data, int startIndex, int elementCount)
					{
						SetData<T>(0, 0, 0, Width, Height, 0, Depth, data, startIndex, elementCount);
					}
		
					public: template<struct T> void SetData (int level,
											int left, int top, int right, int bottom, int front, int back,
											Riccsson::System::Array<T>* data, int startIndex, int elementCount)
					{
						if (data == null) 
							throw Riccsson::System::ArgumentNullException("data");

						var elementSizeInByte = Marshal::SizeOf(typeof(T));
						var dataHandle = GCHandle::Alloc(data, GCHandleType::Pinned);
						var dataPtr = (IntPtr)(dataHandle.AddrOfPinnedObject().ToInt64() + startIndex * elementSizeInByte);
						int width = right - left;
						int height = bottom - top;
						int depth = back - front;

						/*
			#if OPENGL
						GL.BindTexture(glTarget, glTexture);
						GraphicsExtensions.CheckGLError();
						GL.TexSubImage3D(glTarget, level, left, top, front, width, height, depth, glFormat, glType, dataPtr);
						GraphicsExtensions.CheckGLError();
			#elif DIRECTX
						int rowPitch = GetPitch(width);
						int slicePitch = rowPitch * height; // For 3D texture: Size of 2D image.
						var box = new DataBox(dataPtr, rowPitch, slicePitch);

						int subresourceIndex = level;

						var region = new ResourceRegion(left, top, front, right, bottom, back);

						var d3dContext = GraphicsDevice._d3dContext;
						lock (d3dContext)
							d3dContext.UpdateSubresource(box, _texture, subresourceIndex, region);
			#endif
						*/
						dataHandle.Free ();
					}
				};
			}
		}
	}
}

#endif